package cn.lycode.framework.web.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.lycode.common.config.GlobalConfig;
import cn.lycode.common.core.domain.AjaxResult;
import cn.lycode.common.enums.ErrorEnum;
import cn.lycode.common.exception.base.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.util.ArrayList;
import java.util.Objects;

/**
 * @Title: GlobalExceptionHandler
 * @Author lyCode
 * @Date 2024/2/29 16:44
 * @description: 全局异常处理器
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NotLoginException.class)
    @ResponseBody
    public AjaxResult<Object> handlerNotLoginException(NotLoginException e) {
        String message = "";
        if (e.getType().equals(NotLoginException.NOT_TOKEN)) {
            message = "未能读取到有效 token";
        } else if (e.getType().equals(NotLoginException.INVALID_TOKEN) || e.getType().equals(NotLoginException.TOKEN_FREEZE)) {
            message = "您的登录信息已过期，请重新登录以继续访问。";
            // TODO: websocket close
            // sendWsClose();
        } else if (e.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
            message = "token 已过期";
        } else if (e.getType().equals(NotLoginException.BE_REPLACED)) {
            message = "token 已被顶下线";
        } else if (e.getType().equals(NotLoginException.KICK_OUT)) {
            message = "token 已被踢下线";
        } else if (e.getType().equals(NotLoginException.NO_PREFIX)) {
            message = "未按照指定前缀提交 token";
        } else {
            message = "当前会话未登录";
        }
        return AjaxResult.failed(ErrorEnum.TOKEN_INVALID, message, new ArrayList<>());
    }

    @ExceptionHandler(NotPermissionException.class)
    public AjaxResult<Object> handlerNotPermissionException(NotPermissionException e) {
        return AjaxResult.failed(ErrorEnum.NO_PERMISSION);
    }

    /**
     * 拦截404异常
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseBody
    public AjaxResult<Object> handleNoHandlerFoundException(NoHandlerFoundException e){
        return AjaxResult.failed(ErrorEnum.REQUEST_404_ERROR);
    }

    /**
     * 拦截表单参数校验FROM
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public AjaxResult<Object> handleBindException(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        String msg   = Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage();
        return AjaxResult.failed(ErrorEnum.PARAMS_VALID_ERROR, msg);
    }


    /**
     * 拦截JSON参数校验
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public AjaxResult<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        String msg   = Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage();
        return AjaxResult.failed(ErrorEnum.PARAMS_VALID_ERROR, msg);
    }
    /**
     * 拦截参数类型不正确
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public AjaxResult<Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        String msg   = Objects.requireNonNull(e.getMessage());
        return AjaxResult.failed(ErrorEnum.PARAMS_TYPE_ERROR, msg.split(";")[0]);
    }

    /**
     * 拦截请求方法
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public AjaxResult<Object> handleRequestMethodException(HttpRequestMethodNotSupportedException e) {
        String msg   = Objects.requireNonNull(e.getMessage());
        return AjaxResult.failed(ErrorEnum.REQUEST_METHOD_ERROR, msg);
    }


    /**
     * 拦截断言异常
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public AjaxResult<Object> handleIllegalArgumentException(IllegalArgumentException e) {
        String msg   = Objects.requireNonNull(e.getMessage());
        return AjaxResult.failed(ErrorEnum.ASSERT_ARGUMENT_ERROR, msg);
    }


    /**
     * 拦截自定义抛出异常
     */
    @ExceptionHandler(BaseException.class)
    @ResponseBody
    public AjaxResult<Object> handleException(BaseException e) {
        String code = e.getCode();
        String msg = e.getMsg();
        return new AjaxResult<>(code, msg, new ArrayList<>());
    }

    /**
     * 处理所有不可知异常
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public AjaxResult<Object> handleException(Exception e) {
        if (GlobalConfig.debug) {
            e.printStackTrace();
        }
        log.error("系统异常 {}", e.getMessage());
        return AjaxResult.failed(ErrorEnum.SYSTEM_ERROR, e.getMessage());
    }
}
